/**
 * @description 接口-控制器
 * @since 2019-11-12
 * @author Rid King
*/

const ox = require('@daelui/oxjs')
const { header, objecter } = require('@daelui/oxkit')
const Service = require('./service.js')
const Mock = require('./components/mockjs/mock-min')

// 接口返回值类型
const CATS = {
  REQUEST: 'request',
  RESPONSE: 'response'
}

class Controller extends ox.Controller {
  /**
   * @function 构造方法
  */
  constructor (args) {
    super(args)
    // 服务
    this.$service = new Service(args)
  }

  /**
   * @function saas服务
   * @param {Any} data 数据
   * @return {Any}
  */
  saas (action, extender) {
    // 解析数据
    return this.solveAction(action, extender).then(async action => {
      let params = action.params || {}
      let res = { data: [] }
      params.u = (params.u || String(action.req.params[0] || '')).replace(/^\/+/, '').replace(/\/+$/, '')
      // 查询与当前path匹配的已有接口
      if (params.i || params.u) {
        res = await this.getInters(params)
      }
      let inters = res.data
      inters = Array.isArray(inters) ? inters : []
      // 接口匹配
      let method = String(params.m || (action.req || {}).method || 'get').toLowerCase()
      let filters = inters.filter(item => {
        // 请求方式匹配
        let b = item.method === method
        return b
      })
      let inter = filters[0] || inters[0] || {}
      // 是否是响应
      let isResponse = params.c === CATS.REQUEST ? false : true
      let key = isResponse ? CATS.RESPONSE : CATS.REQUEST
      let result = inter[key]
      // 静态数据
      if (inter.isStatic) {
        result = inter.static
      }
      // 动态模拟数据
      else {
        if (result) {
          let options = this.toObject(result)
          options.isXSS = isResponse ? inter.isResXSS : inter.isReqXSS
          options.xssField = isResponse ? inter.resXSSField : inter.reqXSSField
          result = this.getMockData(options)
        }
      }
      result = this.toObject(result || {})
      if (params.f === 'json') {
        result = this.toString(result, null, ' ')
      }
      // 头数据
      let resHeaders = this.toObject(inter.resHeaders)
      if (Array.isArray(resHeaders)) {
        resHeaders = resHeaders.filter(item => String(item.name) !== '' && String(item.name) !== 'undefined' && String(item.name) !== 'null')
        let map = {}
        resHeaders.forEach(item => {
          let value = item.value
          let reqHeaders = action.req.headers
          for (let key in reqHeaders) {
            value = String(value).replace(`{req.headers.${key}}`, reqHeaders[key])
          }
          map[item.name] = value
        })
        resHeaders = map
      }
      resHeaders = objecter.isPlainObject(resHeaders) ? resHeaders : {}

      // 响应数据
      let resData = {
        type: 'PLAINTEXT',
        data: result,
        resHeaders: header.merge({...header.defaults}, resHeaders)
      }

      // 响应延时
      inter.resTime = parseInt(inter.resTime, 10)
      // 接口配置的时间
      if (inter.resTime && inter.resTime > 0) {
        return new Promise((resolve) => {
          setTimeout(function () {
            resolve(resData)
          }, inter.resTime)
        })
      }

      return resData
    })
  }

  /**
   * @function 接口数据模拟服务
   * @param {Request} req // 请求对象
   * @param {Response} res // 响应对象
  */
  mock (req, res) {
    // get参数解析
    let params = URL.parse(req.url, true).query
    // 接口地址
    let url = params.u
    // 数据类型，默认是响应数据
    let c = params.c === CATS.REQUEST ? CATS.REQUEST : CATS.RESPONSE
    // 模拟数据显示格式
    let f = params.f
    // 请求方式
    let method = String(req.method).toLowerCase()
    // 接口请求方式
    let m = params.m || method
    // 场景
    let s = params.s
    // 查询出所有的模块
    this.queryModuleList().then((modules) => {
      // 获取指定接口
      let inter = this.getInter(modules, url, m)
      // 是否是响应
      let isResponse = c === CATS.REQUEST ? false : true
      if (inter) {
        let data = ''
        let options = inter[isResponse ? 'response' : 'request']
        // 根据场景取数据
        if (s || s === 0) {
          let scene = inter.scene
          scene = Array.isArray(scene) ? scene : []
          // 通用场景
          let defNode = null
          // 指定场景
          let activeNode = null
          scene.forEach(node => {
            if (
              !defNode && String(node.isAble) === '1' &&
              String(node.isDefScene) === '1'
            ) {
              defNode = node
            }
            if (
              !activeNode && (String(s) === node.name ||
              String(s) === String(node.id))
            ) {
              activeNode = node
            }
          })
          defNode = defNode || (scene[0] || {})
          // 已匹配数据
          if (activeNode) {
            data = isResponse ? activeNode.response : activeNode.request
          } else if ((String(s) === '1' || String(s) === 'true')) {
            data = isResponse ? defNode.response : defNode.request
          }
        }
        // 按静态数据形式取
        else if (options.isStatic) {
          data = options.static
        }
        // 生成模块数据发送到客户端
        if (!data) {
          options.isXSS = isResponse ? inter.isResXSS : inter.isReqXSS
          options.xssField = isResponse ? inter.resXSSField : inter.reqXSSField
          data = this.getMockData(options)
        }
        if (data && typeof data === 'object' && f === 'json') {
          data = JSON.stringify(data, null, 2)
        }
        else if (typeof data !== 'object') {
          data = String(data)
        }

        // 针对响应
        if (isResponse) {
          // 响应状态
          let status = (inter.response || {}).status
          status = /\d+/.test(status) ? status : '200'
          // 设置响应状态
          res.statusCode = status;
          // 响应头
          let headers = (inter.response || {}).headers
          headers = Array.isArray(headers) ? headers : []
          headers.forEach(item => {
            if (item.name && item.value) {
              // 设置头
              res.header(item.name, item.value)
            }
          })
        }
        // 响应延时
        inter.resTime = parseInt(inter.resTime, 10)
        // 接口配置的时间
        if (inter.resTime && inter.resTime > 0) {
          setTimeout(function () {
            res.send(data)
          }, inter.resTime)
        }
        else {
          // 系统配置的时间
          let resTime = parseInt(mode.resTime, 10)
          if (resTime && resTime > 0) {
            setTimeout(function () {
              res.send(data)
            }, resTime)
          }
          else {
            res.send(data)
          }
        }
      }
      // 接口不存在
      else {
        res.send(application.result({
          success: true,
          msg: 'Interface not found!'
        }))
      }
    })
  }

  // 获取接口
  async getInters (params) {
    params = params || {}
    let url = String(params.u || '').replace(/^\/+/, '').replace(/\/+$/, '')

    let res = await this.queryAll({}, {
      action: {},
      query: {},
      params: {
        id: params.i,
        url: url
      },
      excute: {
        operators: [
          { field: 'id', operator: 'EQUAL', toLowerCase: true },
          { field: 'url', operator: 'LIKE', toLowerCase: true }
        ],
        sort: [
          {field: 'createTime', by: 'desc'},
          {field: 'order', by: 'desc'}
        ]
      }
    })

    if (!res.data.length && url) {
      res = await this.queryAll({}, {
        action: {},
        query: {},
        params: {},
        excute: {
          sort: [
            {field: 'createTime', by: 'desc'},
            {field: 'order', by: 'desc'}
          ]
        }
      })
      if (res.data.length) {
        res.data = res.data.filter(item => {
          let path = item.url
          path = path.replace(/^\/+/, '').replace(/\/+$/, '')
          path = path.replace(/(\{[^\}]+\})\//g, '$1.?').replace(/\{[^\}]+\}/g, '[\\w_-]*')
          let b = new RegExp(path, 'i').test(url)
          return b
        })
      }
    }

    return res
  }

  /**
   * @function 获取模拟数据
   * @param {Object} req // 请求名响应对象
   * @return {Object}
  */
  getMockData (req) {
    let me = this
    let params = req.params
    // 参数通用处理
    params = Array.isArray(params) ? params : []

    // 默认取第一个参数(根参数对象)，生成模板结果
    let param = params[0]
    if (typeof param !== 'object') {
      return {}
    }

    // 根节点参数
    param.name = param.name || 'root'
    // 基本类型
    let baseTypes = ['string', 'number', 'boolean']
    let baseKey = '_#_BASE_#_'
    // 解析结果
    let result = resolveItem(param)
    let tpl = {}
    tpl[result.key] = result.tpl
    // console.log(tpl)
    // 生成模拟数据
    let data = {}
    try {
      data = Mock.mock(tpl)
    } catch (e) {
      data = {}
    }
    // 取出根键对应的值
    data = data[param.name]

    // 数据转换化字符串
    let res = resolveData(data)
    if (typeof res !== 'object') {
      res = String(res)
    }
    // XSS设置
    if (res && typeof res === 'object') {
      res = this.xssReject(res, req)
    }
    return res

    // item解析
    function resolveItem (item) {
      // 模板
      let tpl = {}
      // 参数通用处理
      item = item || {}
      let name = item.name || 'undefinedParam'
      let key = name
      let rule = item.rule || '1'
      let value = item.ruleValue || item.value || ''

      // 参数类型
      let type = me.getParamRuleType(item)
      // 对象类型
      if (type === 'object') {
        key = name
        // 子参数
        if (Array.isArray(item.params)) {
          item.params.forEach(item => {
            let result = resolveItem(item)
            tpl[result.key] = result.tpl
          })
        }
      }
      // 数组类型
      else if (type === 'array') {
        rule = item.rule && item.rule >= '1' ? item.rule : '1-10'
        key = name + '|' + rule
        tpl = []
        // 子参数
        if (Array.isArray(item.params)) {
          item.params.forEach(item => {
            let result = resolveItem(item)
            // 基本类型需要以对象形式解析
            if (baseTypes.indexOf(me.getParamRuleType(item)) > -1) {
              let node = {}
              node[baseKey + result.key] = result.tpl
              tpl.push(node)
            }
            // 其它类型
            else {
              tpl.push(result.tpl)
            }
          })
        }
      }
      // 基本类型
      else if (baseTypes.indexOf(type) > -1) {
        key = name + '|' + rule
        tpl = value
      }
      // 正则类型
      else if (['regexp'].indexOf(type) > -1) {
        key = name + '|' + rule
        try {
          let reg = value.replace(/^\/|\/\w*$/g, '')
          reg = new RegExp(reg)
          value = reg
        } catch (e) {
          value = /[a-z][A-Z][0-9]/
        }
        tpl = value
      }
      // 自定义比例分配
      else if (/ratio/.test(type)) {
        // value示例：0-1,1-8,2-1
        key = name
        let list = []
        let total = 1
        let matches = null
        let reg = /\,?(.+?)-(\d+)/g
        while(matches = reg.exec(value)) {
          let count = parseInt(matches[2], 10)
          list.push({
            value: matches[1],
            start: total,
            end: total + count
          })
          total += count + 1
        }
        tpl = function () {
          let value = ''
          let random = Math.ceil(Math.random() * total)
          list.forEach(item => {
            if (item.start <= random && item.end >= random) {
              value = item.value
            }
          })
          if (/^\d+(\.\d+)?$/.test(value)) {
            value = parseFloat(value)
          }
          return value
        }
      }

      return {key, tpl}
    }

    // 数据解析
    function resolveData (data) {
      if (data && typeof data === 'object') {
        // 数组形式
        if (Array.isArray(data)) {
          data = data.map(item => {
            let value = item
            // 数组类型
            if (Array.isArray(item)) {
              value = resolveData(item)
            }
            // 对象类型
            else if (value && typeof item === 'object') {
              // 是否是基础类型
              let isBase = false
              for (let key in item) {
                if (
                  key.indexOf(baseKey) > -1 && 
                  typeof item[key] !== 'object'
                ) {
                  isBase = true
                  value = item[key]
                  break
                }
                // 非基础类型继续处理
                if (!isBase) {
                  value = resolveData(item)
                }
              }
            }

            return value
          })
        }
        // 对象形式
        else {
          for (let key in data) {
            data[key] = resolveData(data[key])
          }
        }
      }

      return data
    }
    // 示例：
    // Mock.mock({
    //   "string|1-10": "★",
    //   "number|1-100.1-10": 1,
    //   "boolean|1-2": true,
    //   "regexp": "/[a-z][A-Z][0-9]/",
    //   "function": "() => Math.random()",
    //   "array|1-10": [
    //     {
    //       "foo|+1": 3,
    //       "bar|1-10": "★"
    //     }
    //   ],
    //   "items": [
    //     1,
    //     true,
    //     "hello",
    //     "/\\w{10}/"
    //   ],
    //   "object": {
    //     "foo|+1": 2,
    //     "bar|1-10": "★"
    //   },
    //   "placeholder": "@title"
    // })
  }

  /**
   * @function 获取参数模拟类型
   * @param {Object} item 参数对象
   * @return {string}
  */
  getParamRuleType (item) {
    item = item || {}
    return !item.ruleType || item.ruleType === 'default' ? item.type : item.ruleType
  }

  /**
   * @function xss注入
   * @param {any} data 数据
   * @return {any}
  */
  xssReject (data, inter) {
    inter = inter || {}
    // xss字段
    let fileds = String(inter.xssField || '').trim().split(',')
    if (String(inter.isXSS) === '1') {
      // 数据形式
      if (Array.isArray(data)) {
        data = data.map(item => {
          return this.xssReject(item, inter)
        })
      }
      // 对象形式
      else if (data && typeof data === 'object') {
        for (let key in data) {
          let value = data[key]
          // 对象类型
          if (typeof value === 'object' && value !== null) {
            data[key] = this.xssReject(data[key], inter)
          }
          // 全部字段
          else if (
            fileds.length === 0 ||
            fileds.indexOf('*') > -1 ||
            (fileds.length === 1 && fileds[0] === '')
          ) {
            data[key] = data[key] + this.getXSSText()
          }
          // 指定字段
          else {
            if (fileds.indexOf(key) > -1) {
              data[key] = data[key] + this.getXSSText()
            }
          }
        }
      }
    }

    return data
  }

  /**
   * @function 获取XSS文本
  */
  getXSSText () {
    let xss = [
      '<script>alert("XSS")</script>',
      '<img src=1 onerror=alert(1)>',
      '<img src=1 onerror=\u0061\u006c\u0065\u0072\u0074(1)>',
      '<div οnclick=alert("XSS")>',
      '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">xss</a>',
      '<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>',
      '<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></iframe>'
    ]
    let n = Math.floor(xss.length * Math.random())
    return xss[n]
  }

  /**
   * @function 转换成对象
   * @param {Any} data 数据
   * @return {Any}
  */
  toObject (data) {
    let result = data
    if (typeof data === 'string' && /\{|\[/m.test(data)) {
      try {
        result = (new Function('return ' + data))()
      } catch (e) {
        result = data
      }
    }

    return result
  }

  /**
   * @function 转换成字符串
   * @param {Any} data 数据
   * @return {Any}
  */
  toString (data, a1, a2) {
    let result = data
    if (data && typeof data === 'object') {
      try {
        result = JSON.stringify(data, a1, a2)
      } catch (e) {
        result = data
      }
    }

    return result
  }
}

module.exports = Controller